package com.chisheng.caa.service;

import org.apache.poi.ss.formula.eval.NotImplementedException;
import org.springframework.lang.NonNull;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 带有效缓存时间的内存缓存。支持并发访问。
 *
 * @author Chisheng Chen
 */
public class ExpiryMap<K, V> implements Map<K, V> {

    /**
     * 数据缓存池。
     */
    private final ConcurrentMap<K, V> data;

    /**
     * 有效时间缓存池。
     */
    private final ConcurrentMap<K, Long> expiry;

    /**
     * 默认刷新的次数
     */
    private final static long REFRESH_INTERVAL = 60;

    /**
     * 默认的刷新门限
     */
    private final static long REFRESH_THRESHOLD = 1000;

    public ExpiryMap() {
        this.data = new ConcurrentHashMap<>(16);
        this.expiry = new ConcurrentHashMap<>(16);
    }

    @Override
    public int size() {
        return keySet().size();
    }

    @Override
    public boolean isEmpty() {
        return size() == 0;
    }

    @Override
    public boolean containsKey(Object key) {
        if (key != null && expiry.containsKey(key)) {
            boolean invalidKey = isInvalidKey(key);
            if (invalidKey) {
                @SuppressWarnings("all")
                Long $0 = expiry.remove(key);
                @SuppressWarnings("all")
                V $1 = data.remove(key);
            }
            return !invalidKey;
        }
        return false;
    }

    @Override
    public boolean containsValue(Object value) {
        removeInvalidKeys();
        return data.containsValue(value);
    }

    @Override
    public V get(Object key) {
        if (containsKey(key)) {
            return data.get(key);
        }
        return null;
    }

    @Override
    @Deprecated
    public V put(K key, V value) {
        throw new NotImplementedException("请使用 put(K, V, long) 方法替代此方法");
    }

    /**
     * {@link Map#put(Object, Object)}
     *
     * @param expiryTime 键值过期时间（ms）
     */
    public V put(K key, V value, long expiryTime) {
        if (!containsKey(key)) {
            expiry.put(key, System.currentTimeMillis() + expiryTime);
        }
        return data.put(key, value);
    }

    @Override
    public V remove(Object key) {
        if (containsKey(key)) {
            expiry.remove(key);
            return data.remove(key);
        }
        return null;
    }

    @Override
    @Deprecated
    public void putAll(@NonNull Map<? extends K, ? extends V> m) {
        throw new NotImplementedException("此方法未提供实现");
    }

    @Override
    public void clear() {
        expiry.clear();
        data.clear();
    }

    @Override
    public Set<K> keySet() {
        removeInvalidKeys();
        return data.keySet();
    }

    @Override
    public Collection<V> values() {
        removeInvalidKeys();
        return data.values();
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        removeInvalidKeys();
        return data.entrySet();
    }

    /**
     * 定时清除过期的键值
     */
    @SuppressWarnings("unused")
    private void scheduleRemoveInvalidKeys() {
        new Timer().schedule(new TimerTask() {

            /**
             * 扫描次数
             */
            private int count = 0;

            @Override
            public void run() {
                if (++count % REFRESH_INTERVAL == 0 || data.keySet().size() % REFRESH_THRESHOLD == 0) {
                    removeInvalidKeys();
                }
            }
        }, 1000, 1000);
    }

    /**
     * 判断键值是否有效
     */
    private boolean isInvalidKey(Object key) {
        @SuppressWarnings("all")
        Long timeout = expiry.get(key);
        return timeout < System.currentTimeMillis();
    }

    /**
     * 移除过期的的键值工具类
     */
    private void removeInvalidKeys() {
        data.keySet().forEach(key -> {
            if (isInvalidKey(key)) {
                expiry.remove(key);
                data.remove(key);
            }
        });

        System.gc();
    }

}
